/**************************************************************************
 * File Name                   : tls_netconn.c
 * Author                      :
 * Version                     :
 * Date                        :
 * Description                 :
 *
 * Copyright (c) 2014 Winner Microelectronics Co., Ltd.
 * All rights reserved.
 *
 ***************************************************************************/
#include "wm_config.h"

#if TLS_CONFIG_SOCKET_RAW

#    include "lwip/sys.h"
#    include "lwip/tcp.h"
#    include "lwip/tcpip.h"
#    include "lwip/udp.h"
#    include "tls_netconn.h"
#    include "wm_debug.h"
#    include "wm_regs.h"
#    include "wm_sockets.h"
#    include <string.h>

struct tls_netconn *p_net_conn[TLS_MAX_NETCONN_NUM];
#    if CONN_SEM_NOT_FREE
sys_sem_t conn_op_completed[TLS_MAX_NETCONN_NUM] = { NULL };
#    endif

// static err_t net_tcp_sent_cb(void *arg, struct tcp_pcb *pcb, u16_t len);
static void  net_tcp_err_cb(void *arg, err_t err);
static err_t net_tcp_poll_cb(void *arg, struct tcp_pcb *pcb);
static void  net_free_socket(int socketno);

// static err_t net_do_writemore(struct tls_netconn *conn);

#    define BUF_CIRC_CNT_TO_END(head, tail, size)                             \
        ({                                                                    \
            int end = (size) - (tail);                                        \
            int n   = ((head) + end) & ((size) -1);                           \
            n < end ? n : end;                                                \
        })

u32  current_src_ip = 0;
void tls_net_set_sourceip(u32 ipvalue)
{
    current_src_ip = ipvalue;
}

u32 tls_net_get_sourceip(void)
{
    return current_src_ip;
}

struct tls_netconn *get_server_conn(struct tls_netconn *conn)
{
    struct tls_netconn *server_conn = NULL;

    do {
        server_conn = dl_list_first(&conn->list, struct tls_netconn, list);
        conn        = server_conn;
    } while ((server_conn != NULL) && server_conn->client);

    return server_conn;
}

static struct tls_netconn *net_alloc_socket(struct tls_netconn *conn)
{
    int                 sock = -1, i = 0, j = 0;
    u32                 cpu_sr;
    struct tls_netconn *conn_t = NULL;

    for (i = 0; i < TLS_MAX_NETCONN_NUM; i++) {
        if (p_net_conn[i] == NULL) {
            sock = i;
            break;
        }
    }

    if (i == TLS_MAX_NETCONN_NUM) {
        TLS_DBGPRT_ERR("\nnet_alloc_socket error\n");
        return NULL;
    }

    if (conn != NULL) {
        j = dl_list_len(&conn->list);
        if (j >= 4) {
            TLS_DBGPRT_ERR("list len > 4\n");
            sock = -1;
        }
    }
    if (sock < 0) {
        TLS_DBGPRT_ERR("sock < 0\n");
        return NULL;
    }
    cpu_sr = tls_os_set_critical();
    conn_t = tls_mem_alloc(sizeof(struct tls_netconn));
    tls_os_release_critical(cpu_sr);
    if (NULL != conn_t) {
        p_net_conn[sock] = conn_t;
        memset(conn_t, 0, sizeof(struct tls_netconn));
        conn_t->used  = true;
        conn_t->state = NETCONN_STATE_NONE;
        conn_t->skt_num = sock + 1; // TLS_MAX_NETCONN_NUM +
        dl_list_init(&conn_t->list);
#    if CONN_SEM_NOT_FREE
        TLS_DBGPRT_INFO(
            "conn_op_completed[%d]=%x\n", sock, conn_op_completed[sock]);
        if (NULL == conn_op_completed[sock]) {
            if (sys_sem_new(&conn_op_completed[sock], 0) != ERR_OK) {
                net_free_socket(conn_t->skt_num);
            }
        }
#    else
        if (sys_sem_new(&conn_t->op_completed, 0) != ERR_OK) {
            net_free_socket(conn_t->skt_num);
        }
#    endif
    }
    TLS_DBGPRT_INFO("net_alloc_socket conn ptr = 0x%x\n", conn_t);
    return conn_t;

    //    return NULL;
}

static void net_free_socket(int socketno)
{
    int                 index;
    u32                 cpu_sr;
    struct tls_netconn *conn = NULL;

    conn = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        return;
    }
    TLS_DBGPRT_INFO("conn ptr = 0x%x\n", conn);
#    if !CONN_SEM_NOT_FREE
    if (NULL != conn->op_completed) {
        sys_sem_free(&conn->op_completed);
    }
#    endif
    conn->used = false;
    if (conn->client && conn->list.prev != NULL
        && conn->list.prev != &conn->list) {
        TLS_DBGPRT_INFO("del from list.\n");
        cpu_sr = tls_os_set_critical();
        dl_list_del(&conn->list);
        tls_os_release_critical(cpu_sr);
    }
    index = conn->skt_num - 1; // TLS_MAX_NETCONN_NUM -
    if (conn->pcb.tcp) {
        tcp_close(conn->pcb.tcp);
        conn->pcb.tcp = NULL;
    }
    tls_mem_free(conn);
    cpu_sr            = tls_os_set_critical();
    conn              = NULL;
    p_net_conn[index] = NULL;
    tls_os_release_critical(cpu_sr);
}

static void net_send_event_to_hostif(struct tls_netconn *conn, int event)
{
    struct tls_socket_desc *skt_desc = conn->skd;
    TLS_DBGPRT_INFO("skt_desc->state_changed: 0x%x, event=%d\n",
                    skt_desc->state_changed,
                    event);
    if (skt_desc->state_changed) {
        skt_desc->state_changed(conn->skt_num, event, conn->state);
    }
}

static void net_tcp_close_connect(int socketno)
{
    err_t               err;
    struct tls_netconn *conn = NULL;
    conn                     = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        return;
    }
    if (NULL == conn->pcb.tcp) {
        TLS_DBGPRT_ERR("\ntcp=null\n");
        return;
    }
    /* Set back some callback pointers */
    tcp_arg(conn->pcb.tcp, NULL);
    if (conn->pcb.tcp->state == LISTEN) {
        tcp_accept(conn->pcb.tcp, NULL);
    } else {
        /* some callbacks have to be reset if tcp_close is not successful */
        tcp_recv(conn->pcb.tcp, NULL);
        tcp_accept(conn->pcb.tcp, NULL);
        tcp_sent(conn->pcb.tcp, NULL);
        tcp_poll(conn->pcb.tcp, NULL, 4);
        tcp_err(conn->pcb.tcp, NULL);
    }
    err = tcp_close(conn->pcb.tcp);
    if (err)
        err = tcp_shutdown(conn->pcb.tcp, 1, 1);
    if (err == ERR_OK) {
        /* Closing succeeded */
        TLS_DBGPRT_INFO("tcp %d closed\n", conn->skt_num);
        conn->state = NETCONN_STATE_NONE;
        /* Set back some callback pointers as conn is going away */
        conn->pcb.tcp = NULL;
        net_free_socket(socketno);
        /* Trigger select() in socket layer. Make sure everybody notices
           activity on the connection, error first! */
    } else {
        /* Closing failed, restore some of the callbacks */
        /* Closing of listen pcb will never fail! */
        LWIP_ASSERT("Closing a listen pcb may not fail!",
                    (conn->pcb.tcp->state != LISTEN));
        // tcp_sent(conn->pcb.tcp, net_tcp_sent_cb);
        tcp_poll(conn->pcb.tcp, net_tcp_poll_cb, 4);
        tcp_err(conn->pcb.tcp, net_tcp_err_cb);
        tcp_arg(conn->pcb.tcp, (void *) socketno);
        /* don't restore recv callback: we don't want to receive any more data
         */
    }
}

static err_t net_tcp_poll_cb(void *arg, struct tcp_pcb *pcb)
{
    err_t               err_ret     = ERR_OK;
    struct tls_netconn *conn        = NULL;
    struct tls_netconn *server_conn = NULL;
    int                 socketno    = (int) arg;
    conn                            = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used || NULL == pcb) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d,pcb=%x\n", conn, conn->used, pcb);
        return ERR_ARG;
    }
    // TLS_DBGPRT_INFO("net_tcp_poll_cb start\n");
    /*
        if (conn == NULL || pcb == NULL) {
        TLS_DBGPRT_ERR("\npcb=null\n");
            return ERR_CLSD;
        }
    */
    if (conn->write_state && (conn->state == NETCONN_STATE_CONNECTED)) {
        // net_do_writemore(conn);
    } else if (conn->state == NETCONN_STATE_CLOSED) {
        net_tcp_close_connect(socketno);
    } else {
        if ((pcb->state == CLOSE_WAIT) || (pcb->state == CLOSED)) {
            if (conn->pcb.tcp != NULL) {
                tcp_arg(conn->pcb.tcp, NULL);
                tcp_accept(conn->pcb.tcp, NULL);
                tcp_recv(conn->pcb.tcp, NULL);
                tcp_sent(conn->pcb.tcp, NULL);
                tcp_poll(conn->pcb.tcp, NULL, 4);
                tcp_err(conn->pcb.tcp, NULL);
                tcp_close(conn->pcb.tcp);
                conn->state = NETCONN_STATE_NONE;
                net_send_event_to_hostif(conn, NET_EVENT_TCP_DISCONNECT);
                conn->pcb.tcp = NULL;
                net_free_socket(socketno);
            }
        }
    }

    if (conn) {
        if (conn->client && conn->idle_time > 0) {
            TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n",
                            conn->skt_num,
                            conn->client);
            //		server_conn = dl_list_first(&conn->list, struct tls_netconn,
            //list);
            server_conn = get_server_conn(conn);
            if (server_conn) {
                --conn->idle_time;
                if (conn->idle_time == 0) {
                    tcp_abort(pcb);
                    // tls_socket_close(conn->skt_num);
                    TLS_DBGPRT_INFO("update conn->idle_time %d\n",
                                    conn->idle_time);
                    return ERR_ABRT;
                }
            }
        }
        if ((conn->skd) != NULL && (conn->skd->pollf) != NULL) {
            err_ret = conn->skd->pollf(conn->skt_num);
            if (err_ret == ERR_ABRT)
                tcp_abort(pcb);
            return err_ret;
        }
    }

    return ERR_OK;
}

static void net_tcp_err_cb(void *arg, err_t err)
{
    struct tls_netconn *conn     = NULL;
    struct tcp_pcb *    pcb      = NULL;
    int                 socketno = (int) arg;
    u8                  event    = NET_EVENT_TCP_CONNECT_FAILED;

    conn = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        return;
    }
    pcb = conn->pcb.tcp;
    TLS_DBGPRT_INFO("tcp err = %d, pcb==%x, conn==%x, skt==%x\n",
                    err,
                    (char *) pcb,
                    (char *) conn,
                    conn->skd);

    if (pcb) {
        tcp_arg(pcb, NULL);
        tcp_sent(pcb, NULL);
        tcp_recv(pcb, NULL);
        tcp_err(pcb, NULL);
        if (!conn->client) {
            tcp_accept(pcb, NULL);
        }
        if (err == ERR_OK) {
            tcp_close(pcb);
        }
        if (conn->state != NETCONN_STATE_NONE) {
            conn->state = NETCONN_STATE_NONE;
            event       = NET_EVENT_TCP_DISCONNECT;
        }

        net_send_event_to_hostif(conn, event);
        if (conn->skd->errf != NULL) {
            conn->skd->errf(conn->skt_num, err);
        }
        conn->pcb.tcp = NULL;
        net_free_socket(socketno);
        // printf("\nfree socket\n");
    }
}

#    if (RAW_SOCKET_USE_CUSTOM_PBUF)
static struct raw_sk_pbuf_custom *raw_sk_alloc_pbuf_custom(void)
{
    return (struct raw_sk_pbuf_custom *) mem_malloc(
        sizeof(struct raw_sk_pbuf_custom));
}

static void raw_sk_free_pbuf_custom(struct raw_sk_pbuf_custom *p)
{
    if (p != NULL) {
        mem_free(p);
        p = NULL;
    }
}

static void raw_sk_free_pbuf_custom_fn(struct pbuf *p)
{
    struct raw_sk_pbuf_custom *pcr = (struct raw_sk_pbuf_custom *) p;

    if (p != NULL) {
        // printf("\nraw_sk_free_pbuf_custom_fn,len=%d, custom pcb=%x, conn=%x,
        // conn pcb=%x\n\n",p->tot_len, pcr->pcb, pcr->conn, ((struct
        // tls_netconn *)pcr->conn)->pcb.tcp);
        if (TRUE == ((struct tls_netconn *) pcr->conn)->used
            && pcr->pcb == ((struct tls_netconn *) pcr->conn)->pcb.tcp) {
            tcp_recved((struct tcp_pcb *) pcr->pcb, p->tot_len);
        }

        if (pcr->original != NULL) {
            //		printf("\ngo to pbuf_free,original
            //pbuf=%x\n",pcr->original);
            pbuf_free(pcr->original);
        }
        raw_sk_free_pbuf_custom(pcr);
    }
}

#    endif

/**
 * Receive callback function for TCP connect.
 */
static err_t
net_tcp_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
    struct tls_netconn *conn;
    struct tls_netconn *server_conn = NULL;
    err_t               err_ret     = ERR_OK;
    u16                 datalen     = 0;
#    if (RAW_SOCKET_USE_CUSTOM_PBUF)
    struct raw_sk_pbuf_custom *pcr     = NULL;
    struct pbuf *              newpbuf = NULL;
#    endif
    struct pbuf *p_tmp, *p_next;
    int          socketno = -1;

    //   TLS_DBGPRT_INFO("net_tcp_recv_cb err=%d\n", err);

    LWIP_UNUSED_ARG(pcb);
    LWIP_ASSERT("must have a pcb argument", pcb != NULL);
    LWIP_ASSERT("must have an argument", arg < 0);
    socketno = (int) arg;
    conn     = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        return ERR_ARG;
    }
    LWIP_ASSERT("tcp recv for wrong pcb!", conn->pcb.tcp == pcb);

    if (err) {
        /* tcp is disconnect */
        TLS_DBGPRT_INFO("err code = %d\n", err);
        err_ret = err;
#    if 0
        if (conn->pcb.tcp != NULL) {
            tcp_arg(conn->pcb.tcp, NULL);
            tcp_accept(conn->pcb.tcp, NULL);
            tcp_recv(conn->pcb.tcp, NULL);      
            tcp_sent(conn->pcb.tcp, NULL);
            tcp_poll(conn->pcb.tcp, NULL, 4);
            tcp_err(conn->pcb.tcp, NULL);
            tcp_close(conn->pcb.tcp);     
            conn->state = NETCONN_STATE_NONE;         
            net_send_event_to_hostif(conn, NET_EVENT_TCP_DISCONNECT);
            conn->pcb.tcp = NULL;
            net_free_socket(conn);
        }
#    endif
        if (conn->skd != NULL && conn->skd->recvf != NULL) {
            TLS_DBGPRT_INFO("conn->skd->recvf to call.");
            err_ret = conn->skd->recvf(conn->skt_num, p, err);
            if (err_ret == ERR_ABRT)
                tcp_abort(pcb);
            p = NULL;
        }

        if (p) {
            pbuf_free(p);
            p = NULL;
        }
        return err_ret;
    }

    if (pcb == NULL || conn == NULL) { // p == NULL ||
        TLS_DBGPRT_INFO("pcb = 0x%x, p = 0x%x\n", pcb, p);
        return ERR_VAL;
    }

    if (p == NULL) {
        TLS_DBGPRT_ERR("received 0\n");
        net_tcp_err_cb((void *) socketno, ERR_OK);
        return ERR_OK;
    }
#    if 0 //(RAW_SOCKET_USE_CUSTOM_PBUF)
	pcr = raw_sk_alloc_pbuf_custom();
	if(pcr == NULL)
	{
		return ERR_MEM;
	}
	//newpbuf = pbuf_alloced_custom(PBUF_RAW, p->tot_len, PBUF_REF, &pcr->pc, p->payload, p->tot_len);

	newpbuf = &(pcr->pc);
	newpbuf->len = 0;
	newpbuf->payload = NULL;
	newpbuf->tot_len = p->tot_len;
	newpbuf->next = p;
	newpbuf->flags = PBUF_FLAG_IS_CUSTOM;
	newpbuf->type = PBUF_REF;
	newpbuf->ref =1;

      if (newpbuf == NULL) {
		raw_sk_free_pbuf_custom(pcr);
		return ERR_MEM;
      }
	pcr->original = p;
	pcr->param = pcb;
	pcr->pc.custom_free_function = raw_sk_free_pbuf_custom_fn;
#    endif
#    if !RAW_SOCKET_USE_CUSTOM_PBUF
    tcp_recved(pcb, p->tot_len);
#    endif

    if (conn->client && conn->idle_time > 0) {
        TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n",
                        conn->skt_num,
                        conn->client);
        //		server_conn = dl_list_first(&conn->list, struct tls_netconn,
        //list);
        server_conn = get_server_conn(conn);
        TLS_DBGPRT_INFO("server_conn=%p\n", server_conn);
        if (server_conn) {
            conn->idle_time = server_conn->idle_time;
            TLS_DBGPRT_INFO("update conn->idle_time %d\n", conn->idle_time);
        }
    }
    if (conn->skd->recvf != NULL) {
        tls_net_set_sourceip(ip_addr_get_ip4_u32(&pcb->remote_ip));
        datalen = p->tot_len;
        if (conn->skd->recvwithipf != NULL) {
            // conn->skd->recvwithipf(conn->skt_num, datalen, (u8
            // *)(&(pcb->remote_ip.addr)), pcb->remote_port, ERR_OK);
            err_ret = conn->skd->recvwithipf(
                conn->skt_num,
                datalen,
                (u8 *) (&(ip_2_ip4(&pcb->remote_ip)->addr)),
                pcb->remote_port,
                ERR_OK);
            if (err_ret == ERR_ABRT)
                tcp_abort(pcb);
        }

        p_next = p;
        for (p_tmp = p; p_tmp != NULL;) {
            p_next      = p_tmp->next;
            p_tmp->next = NULL;
            if (err_ret == ERR_ABRT) {
                pbuf_free(p_tmp);
            } else if (err_ret == ERR_OK) {
                if (p_next != NULL)
                    printf("\npbufcat p next=%x\n", (u32) p_next);
#    if (RAW_SOCKET_USE_CUSTOM_PBUF)
                pcr = raw_sk_alloc_pbuf_custom();
                if (pcr == NULL) {
                    return ERR_MEM;
                }

                newpbuf = pbuf_alloced_custom(PBUF_RAW,
                                              p_tmp->len,
                                              PBUF_REF,
                                              &pcr->pc,
                                              p_tmp->payload,
                                              p_tmp->len);
                if (newpbuf == NULL) {
                    raw_sk_free_pbuf_custom(pcr);
                    return ERR_MEM;
                }
                pcr->original                = p_tmp;
                pcr->conn                    = conn;
                pcr->pcb                     = pcb;
                pcr->pc.custom_free_function = raw_sk_free_pbuf_custom_fn;

                err_ret = conn->skd->recvf(conn->skt_num, newpbuf, ERR_OK);
#    else
                err_ret = conn->skd->recvf(conn->skt_num, p_tmp, ERR_OK);
#    endif
                if (err_ret == ERR_ABRT)
                    tcp_abort(pcb);
            }
            p_tmp = p_next;
        }

    } else {
#    if 0 //(RAW_SOCKET_USE_CUSTOM_PBUF)
	raw_sk_free_pbuf_custom(pcr);
#    endif
        if (p)
            pbuf_free(p);
    }

    return err_ret;
}

#    if 0
static err_t net_tcp_sent_cb(void *arg, struct tcp_pcb *pcb, u16_t len)
{
    struct tls_netconn *conn = (struct tls_netconn *)arg;    

    //TLS_DBGPRT_INFO("s\n");
    if (conn->write_state && (conn->state == NETCONN_STATE_CONNECTED)) {
        net_do_writemore(conn);
    } else if (conn->state == NETCONN_STATE_CLOSED) {
        net_tcp_close_connect(conn);
    }

    return ERR_OK;
}
#    endif

/**
 * tcp connnect callback
 */
static err_t net_tcp_connect_cb(void *arg, struct tcp_pcb *pcb, err_t err)
{
    struct tls_netconn *conn;
    err_t               err_ret  = ERR_OK;
    int                 socketno = -1;

    socketno = (int) arg;
    conn     = tls_net_get_socket(socketno);
    if (conn == NULL || conn->used != TRUE) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        tcp_abort(pcb);
        return ERR_ABRT;
    }
    // pcb = conn->pcb.tcp;
    if ((conn->proto == TLS_NETCONN_TCP) && (err == ERR_OK)) {
        TLS_DBGPRT_INFO("net_tcp_connect_cb =====> state : %d\n", pcb->state);

        conn->state = NETCONN_STATE_CONNECTED;
        net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECTED);
    } else {
        TLS_DBGPRT_INFO("the err is =%d\n", err);
    }

    if (conn->skd != NULL && conn->skd->connf != NULL) {
        err_ret = conn->skd->connf(conn->skt_num, err);
        if (err_ret == ERR_ABRT)
            tcp_abort(pcb);
        return err_ret;
    }

    return err;
}

/**
 * Accept callback function for TCP netconns.
 */
static err_t net_tcp_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err)
{
    err_t               err_ret = ERR_OK;
    struct tls_netconn *conn    = NULL;
    struct tls_netconn *newconn;
    struct tcp_pcb *    pcb;
    u32                 cpu_sr;
    int                 socketno = -1;

    // TLS_DBGPRT_INFO("=====>\n");
#    if 0
    if (conn->tcp_connect_cnt >= TLS_MAX_SOCKET_NUM) {
        return ERR_ABRT;
    }
#    endif
    socketno = (int) arg;
    conn     = tls_net_get_socket(socketno);
    if (conn == NULL || conn->used != TRUE) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        tcp_abort(newpcb);
        return ERR_ABRT;
    }

    if (conn) {
        tcp_accepted(conn->pcb.tcp);
    }

    newconn = net_alloc_socket(conn);
    // printf("\nnew conn==%x, new pcb==%x\n",newconn,newpcb);
    if (!newconn) {
        /* connection aborted */
        /* not happen */
        tcp_abort(newpcb);
        // printf("\r\nnet_tcp_accept_cb err\r\n");
        return ERR_ABRT;
    }

    // conn->tcp_connect_cnt++;

    newconn->used      = true;
    newconn->pcb.tcp   = newpcb;
    newconn->client    = true;
    newconn->idle_time = conn->idle_time;
    newconn->proto     = TLS_NETCONN_TCP;
    newconn->state     = NETCONN_STATE_CONNECTED;
    pcb                = newconn->pcb.tcp;
    ip_set_option(newconn->pcb.tcp, SOF_KEEPALIVE);
    newconn->skd = conn->skd;
    tcp_arg(pcb, (void *) (newconn->skt_num));
    tcp_recv(pcb, net_tcp_recv_cb);
    // tcp_sent(pcb, net_tcp_sent_cb);
    tcp_poll(pcb, net_tcp_poll_cb, 2);
    tcp_err(pcb, net_tcp_err_cb);
    cpu_sr = tls_os_set_critical();
    dl_list_add_tail(&conn->list, &newconn->list);
    tls_os_release_critical(cpu_sr);
    newconn->port      = host_to_le16(pcb->remote_port);
    newconn->localport = conn->localport;
    //   ip_addr_set(&newconn->addr, &pcb->remote_ip);
    ip_addr_copy(newconn->addr, pcb->remote_ip);
    net_send_event_to_hostif(newconn, NET_EVENT_TCP_JOINED);
    if (conn->skd->acceptf != NULL) {
        err_ret = conn->skd->acceptf(newconn->skt_num, err);
        if (err_ret == ERR_ABRT) {
            tcp_abort(pcb);
        }
    }
    // conn->state = NETCONN_STATE_CONNECTED;

    return err_ret;
}

/**
 * Receive callback function for UDP netconns.
 */
static void net_udp_recv_cb(void *           arg,
                            struct udp_pcb * pcb,
                            struct pbuf *    p,
                            const ip_addr_t *srcaddr,
                            u16_t            port)
{
    struct tls_netconn *conn;
    u32                 datalen  = 0;
    int                 socketno = -1;

    // TLS_DBGPRT_INFO("=====>\n");
    LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
    LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
    LWIP_ASSERT("recv_udp must have an argument", arg < 0);
    socketno = (int) arg;
    conn     = tls_net_get_socket(socketno);
    if (conn == NULL || conn->used != TRUE || NULL == pcb) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        if (p != NULL)
            pbuf_free(p);
        return;
    }
    LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
    /*
        if (conn == NULL || pcb == NULL) {
        TLS_DBGPRT_INFO("if (conn == NULL || pcb == NULL) \n");
            if (p != NULL)
                pbuf_free(p);
            return;
        }
    */
    if (conn->skd->recvf != NULL) {
        datalen = p->tot_len;
        /*if Address is broadcast, update source IP according to source address
         * and source port according to sender*/

        //		if ((conn->addr.addr == IPADDR_BROADCAST)||((conn->addr.addr &
        //0xFF) == 0xFF))
        if ((ip_addr_get_ip4_u32(&conn->addr) == IPADDR_BROADCAST)
            || ((ip_addr_get_ip4_u32(&conn->addr) & 0xFF) == 0xFF)) {
            tls_net_set_sourceip(ip_addr_get_ip4_u32(srcaddr));
            conn->port = port;
        } else {
            tls_net_set_sourceip(ip_addr_get_ip4_u32(&conn->addr));
        }

        if (conn->skd->recvwithipf != NULL) {
            conn->skd->recvwithipf(conn->skt_num,
                                   datalen,
                                   (u8 *) (&(ip_2_ip4(srcaddr)->addr)),
                                   port,
                                   ERR_OK);
        }

        conn->skd->recvf(conn->skt_num, p, ERR_OK);
    } else {
        if (p) {
            pbuf_free(p);
            p = NULL;
        }
    }

    return;
}

/**
 * Start create TCP connection
 */
static err_t net_tcp_start(struct tls_netconn *conn)
{
    struct tcp_pcb *pcb;
    err_t           err;
    u16             localportnum = conn->localport;

    // TLS_DBGPRT_INFO("=====>\n");
    conn->pcb.tcp = tcp_new();
    pcb           = conn->pcb.tcp;

    if (pcb == NULL) {
        TLS_DBGPRT_INFO("could not allocate tcp pcb\n");
        return ERR_VAL;
    }

    tcp_arg(pcb, (void *) (conn->skt_num));

    if (conn->client) {
        TLS_DBGPRT_INFO(
            "pcb = 0x%x, conn->addr = 0x%x, port = %d, localport=%d\n",
            pcb,
            ip_addr_get_ip4_u32(&conn->addr),
            conn->port,
            conn->localport);
        tcp_err(pcb, net_tcp_err_cb);
        tcp_recv(pcb, net_tcp_recv_cb);
        // tcp_nagle_disable(pcb);
        if (pcb->recv != NULL) {
            TLS_DBGPRT_INFO("pcb->recv != NULL\n");
        }
        // tcp_sent(pcb, net_tcp_sent_cb);
        tcp_poll(pcb, net_tcp_poll_cb, 4);
        ip_set_option(pcb, SOF_KEEPALIVE);
        if (localportnum > 0 && localportnum <= 0xFFFF) {
            err = tcp_bind(pcb, IP_ADDR_ANY, localportnum);
            if (err != ERR_OK) {
                TLS_DBGPRT_INFO("tcp bind failed %d\n", err);
                return err;
            }
        }
        err = tcp_connect(pcb, &conn->addr, conn->port, net_tcp_connect_cb);
        if (err != ERR_OK) {
            TLS_DBGPRT_INFO("tcp connect failed %d\n", err);
            return err;
        }
        if (conn->localport == 0) {
            conn->localport = pcb->local_port;
        }
    } else {
        TLS_DBGPRT_INFO("pcb ptr = 0x%x, conn->port = %d\n", pcb, conn->port);
        ip_set_option(pcb, SOF_REUSEADDR);
        err = tcp_bind(pcb, IP_ADDR_ANY, conn->port);
        if (err != ERR_OK) {
            TLS_DBGPRT_INFO("tcp bind failed %d\n", err);
            return err;
        }
        conn->pcb.tcp = tcp_listen(pcb);
        // ip_set_option(conn->pcb.tcp, SOF_KEEPALIVE);
        if (conn->pcb.tcp == NULL) {
            tcp_close(pcb);
            /* create tcp sever failed */
            TLS_DBGPRT_INFO("tcp listen failed\n");
            return ERR_VAL;
        }
        tcp_arg(conn->pcb.tcp, (void *) (conn->skt_num));
        tcp_accept(conn->pcb.tcp, net_tcp_accept_cb);
    }

    return ERR_OK;
}

/**
 * Start create UDP connection
 */
static err_t net_udp_start(struct tls_netconn *conn)
{
    struct udp_pcb *udppcb;
    u16             localportnum = conn->localport;

    udppcb = udp_new();

    if (udppcb == NULL) {
        return ERR_MEM;
    }
    conn->pcb.udp = udppcb;
    if (udp_bind(udppcb, IP_ADDR_ANY, localportnum) != ERR_OK) {
        TLS_DBGPRT_INFO("udp connect failed\n");
        return ERR_CONN;
    }

    if (conn->localport == 0) {
        conn->localport = udppcb->local_port;
    }

    /* Set a receive callback for the upcb */
    udp_recv(udppcb, net_udp_recv_cb, (void *) (conn->skt_num));

    return ERR_OK;
}
#    if 0
static err_t net_do_writemore(struct tls_netconn *conn)
{
    err_t err = ERR_OK;
    void *dataptr;
    u16 len, available;
    u8 write_finished = 0;
    u32 diff;
    u8 apiflags = TCP_WRITE_FLAG_COPY;
    u8 dontblock = 0;    

    dataptr = (u8_t*)conn->current_msg->dataptr + conn->current_msg->write_offset;
    diff = conn->current_msg->len - conn->current_msg->write_offset;
    if (diff > 0xffffUL) { /* max_u16_t */
        len = 0xffff;
        apiflags |= TCP_WRITE_FLAG_MORE;
    } else {
        len = (u16_t)diff;
    }
    available = tcp_sndbuf(conn->pcb.tcp);
    if (available < len) {
        /* don't try to write more than sendbuf */
        len = available;
        if (dontblock){ 
            if (!len) {
                err = ERR_WOULDBLOCK;
                goto err_mem;
            }
        } else {
            apiflags |= TCP_WRITE_FLAG_MORE;
        }
    }
    err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);

err_mem:
    if (err == ERR_OK) {
        conn->current_msg->write_offset += len;
        if ((conn->current_msg->write_offset == conn->current_msg->len) || dontblock) {
            /* everything was written */
            write_finished = 1;
            conn->current_msg->write_offset = 0;
        }
        tcp_output(conn->pcb.tcp);
    } else if ((err == ERR_MEM) && !dontblock) {
        /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
           we do NOT return to the application thread, since ERR_MEM is
           only a temporary error! */

        /* tcp_write returned ERR_MEM, try tcp_output anyway */
        tcp_output(conn->pcb.tcp);

    } else {
        /* On errors != ERR_MEM, we don't try writing any more but return
           the error to the application thread. */
        write_finished = 1;
        conn->current_msg->len = 0;
    }
    if (write_finished) {
        /* everything was written: set back connection state
           and back to application task */
        conn->current_msg->err = err;
        conn->write_state = false;

        tls_mem_free(conn->current_msg);
        conn->current_msg = NULL;
    }
    return ERR_OK;
}
#    endif

static err_t net_skt_tcp_send(struct tls_net_msg *net_msg)
{
    struct tcp_pcb *    pcb = NULL;
    struct tls_netconn *conn;
    err_t               err;
    // TLS_DBGPRT_INFO("=====>\n");
    conn = tls_net_get_socket(net_msg->skt_no);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("conn =%x,used=%d\n", conn, conn->used);
        return ERR_ARG;
    }
    pcb = conn->pcb.tcp;
    /*
        When tcp error occured, lwip will delete the pcb and sometimes GSKT.
        This function maybe registered by GSKT_TimerSend, so we must check if
       GSKT has been delted!!!
    */
    err = tcp_write(pcb, net_msg->dataptr, net_msg->len, TCP_WRITE_FLAG_COPY);
    if (err == ERR_OK) {
        // sys_sem_signal(&net_msg->conn->op_completed);
        tcp_output(pcb);
    } else {
        //	TLS_DBGPRT_INFO("err:%d\n", err);
    }
    return err;
}

/**
 * Send data on a UDP pcb
 */
static void net_do_send(void *ctx)
{
    struct tls_net_msg *msg  = (struct tls_net_msg *) ctx;
    struct tls_netconn *conn = NULL;
    struct pbuf *       p;
    int                 socketno = msg->skt_no;

    // TLS_DBGPRT_INFO("=====>\n");

    p    = msg->p;
    conn = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("conn =%x,used=%d\n", conn, conn->used);
        if (p)
            pbuf_free(p);
#    if CONN_SEM_NOT_FREE
        sys_sem_signal(&conn_op_completed[socketno - 1]);
#    endif

        return;
    }
#    if LWIP_CHECKSUM_ON_COPY
    if (ip_addr_isany(&msg->addr)) {
        msg->err = udp_send_chksum(conn->pcb.udp, p, 0, 0);
    } else {
        msg->err
            = udp_sendto_chksum(conn->pcb.udp, p, &msg->addr, msg->port, 0, 0);
    }
#    else  /* LWIP_CHECKSUM_ON_COPY */
    if (ip_addr_isany(&msg->addr)) {
        msg->err = udp_send(conn->pcb.udp, p);
    } else {
        msg->err = udp_sendto(conn->pcb.udp, p, &msg->addr, msg->port);
    }
#    endif /* LWIP_CHECKSUM_ON_COPY */

    pbuf_free(p);
#    if CONN_SEM_NOT_FREE
    sys_sem_signal(&conn_op_completed[socketno - 1]);
#    else
    conn = tls_net_get_socket(socketno);
    if (conn && TRUE == conn->used) {
        sys_sem_signal(&conn->op_completed);
    }
#    endif
}

/**
 * Send data on a TCP pcb
 */
static void net_do_write(void *ctx)
{
    struct tls_net_msg *net_msg     = (struct tls_net_msg *) ctx;
    struct tls_netconn *conn        = NULL;
    struct tls_netconn *server_conn = NULL;

    int socketno = -1;
    // TLS_DBGPRT_INFO("=====>\n");
    socketno = net_msg->skt_no;
    conn     = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("\n conn=%x,used=%d\n", conn, conn->used);
#    if CONN_SEM_NOT_FREE
        sys_sem_signal(&conn_op_completed[socketno - 1]);
#    endif
        return;
    }

    // TLS_DBGPRT_INFO("s=%d,p=0x%x\n", conn->state, conn->pcb.tcp);

#    if 0
    if (ERR_IS_FATAL(conn->last_err)) {
        net_msg->err = conn->last_err;
    }
#    endif
    if (conn->proto == TLS_NETCONN_TCP) {
#    if LWIP_TCP
        if (conn->state != NETCONN_STATE_CONNECTED) {
            /* netconn is connecting, closing or in blocking write */
            net_msg->err = ERR_INPROGRESS;
        } else if (conn->pcb.tcp != NULL) {
            conn->write_state = true;
            // conn->write_offset = 0;
            net_msg->err = net_skt_tcp_send(net_msg);
            /* for both cases: if do_writemore was called, don't ACK the APIMSG
               since do_writemore ACKs it! */
        } else {
            net_msg->err = ERR_CONN;
            TLS_DBGPRT_INFO("==>err=%d\n", net_msg->err);
        }
        if (conn->client && conn->idle_time > 0) {
            TLS_DBGPRT_INFO("conn->skt_num=%d, conn->client=%d\n",
                            conn->skt_num,
                            conn->client);
            //		server_conn = dl_list_first(&conn->list, struct tls_netconn,
            //list);
            server_conn = get_server_conn(conn);
            TLS_DBGPRT_INFO("server_conn=%p\n", server_conn);
            if (server_conn) {
                conn->idle_time = server_conn->idle_time;
                TLS_DBGPRT_INFO("update conn->idle_time %d\n",
                                conn->idle_time);
            }
        }
#    else  /* LWIP_TCP */
        net_msg->err = ERR_VAL;
#    endif /* LWIP_TCP */
#    if (LWIP_UDP || LWIP_RAW)
    } else {
        net_msg->err = ERR_VAL;
#    endif /* (LWIP_UDP || LWIP_RAW) */
    }
    // tls_mem_free(net_msg->dataptr);
    // if(net_msg->err != ERR_OK)
    {
        // TLS_DBGPRT_INFO("conn->proto=%d, err=%d\n", conn->proto,
        // net_msg->err); TLS_DBGPRT_INFO("free net_msg->dataptr=%p\n",
        // net_msg->dataptr);

#    if CONN_SEM_NOT_FREE
        sys_sem_signal(&conn_op_completed[socketno - 1]);
#    else
        conn = tls_net_get_socket(socketno);
        if (conn && TRUE == conn->used) {
            sys_sem_signal(&conn->op_completed);
        }
#    endif
    }
#    if 0
    tls_mem_free(net_msg);
#    endif
}

static void do_create_connect(void *ctx)
{
    struct tls_net_msg *net_msg = (struct tls_net_msg *) ctx;
    struct tls_netconn *conn;
    err_t               err;
    int                 socketno = -1;
    // TLS_DBGPRT_INFO("=====>\n");
    socketno = net_msg->skt_no;
    conn     = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        tls_mem_free(net_msg);
        TLS_DBGPRT_ERR("\nconn=%x,usde=%d\n", conn, conn->used);
        return;
    }
    TLS_DBGPRT_INFO("conn ptr = 0x%x, conn->skt_num=%d, conn->client=%d\n",
                    conn,
                    conn->skt_num,
                    conn->client);

    switch (conn->proto) {
    case TLS_NETCONN_UDP:
        err = net_udp_start(conn);
        if (err != ERR_OK) {
            conn->state = NETCONN_STATE_NONE;
            net_send_event_to_hostif(conn, NET_EVENT_UDP_START_FAILED);
            net_free_socket(socketno);
        } else {
            conn->state = NETCONN_STATE_CONNECTED;
            net_send_event_to_hostif(conn, NET_EVENT_UDP_START);
        }
        break;
    case TLS_NETCONN_TCP:
        err = net_tcp_start(conn);
        if (err != ERR_OK) {
            conn->state = NETCONN_STATE_NONE;
            net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECT_FAILED);
            net_free_socket(socketno);
        } else {
            if (!conn->client) {
                net_send_event_to_hostif(conn, NET_EVENT_TCP_CONNECTED);
            }
            // conn->state = NETCONN_STATE_CONNECTED;
        }
        break;
    default:
        /* Unsupported netconn type, e.g. protocol disabled */
        break;
    }
    tls_mem_free(net_msg);
    return;
}

static void do_close_connect(void *ctx)
{
    struct tls_net_msg *net_msg = (struct tls_net_msg *) ctx;
    struct tls_netconn *conn    = NULL;
    struct tls_netconn *client_conn;
    int                 socketno = net_msg->skt_no;
    int                 i;
    int sktNums[TLS_MAX_SOCKET_NUM] = { -1 }; // 2*TLS_MAX_NETCONN_NUM

    conn = tls_net_get_socket(socketno);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("conn==%x,used=%d\n", conn, conn->used);
        tls_mem_free(net_msg);
        return;
    }

    switch (conn->proto) {
    case TLS_NETCONN_UDP:
        if (conn->pcb.udp != NULL) {
            conn->pcb.udp->recv_arg = NULL;
            udp_remove(conn->pcb.udp);
            conn->pcb.udp = NULL;
        }
        conn->state = NETCONN_STATE_CLOSED;
        break;
    case TLS_NETCONN_TCP:
        if (!conn->client) {
            /* it's a server, close connected client */
            i = 0;
            dl_list_for_each(
                client_conn, &conn->list, struct tls_netconn, list)
            {
                if (client_conn->used) {
                    client_conn->state = NETCONN_STATE_CLOSED;
                    sktNums[i++]       = client_conn->skt_num;
                }
            }

            while (i-- > 0) {
                // client_conn = tls_net_get_socket(sktNums[i]);
                net_tcp_close_connect(sktNums[i]);
            }
        }

        conn->state = NETCONN_STATE_CLOSED;
        net_tcp_close_connect(socketno);
        break;
    default:
        break;
    }
    net_free_socket(socketno);
    tls_mem_free(net_msg);
}

int tls_socket_create(struct tls_socket_desc *skd)
{
    struct tls_netconn *conn = NULL;
    struct tls_net_msg *net_msg;
    err_t               err = ERR_OK;

    // TLS_DBGPRT_INFO("=====>socket_type=%d, skd->protocol=%d\n", socket_type,
    // skd->protocol);

    if ((skd->protocol == SOCKET_PROTO_TCP)
        || (skd->protocol == SOCKET_PROTO_UDP)) {
        conn = net_alloc_socket(NULL);

        if (conn) {
            conn->client = (skd->cs_mode == SOCKET_CS_MODE_CLIENT);
            conn->proto  = skd->protocol;
            if (conn->client) {
                IP_SET_TYPE(&skd->ip_addr, IPADDR_TYPE_V4);
                ip_addr_copy(conn->addr, skd->ip_addr);
                conn->state = NETCONN_STATE_NONE;
            } else {
                if (conn->proto == TLS_NETCONN_UDP)
                    ip_addr_set(&conn->addr, IP_ADDR_BROADCAST);
                else
                    ip_addr_set_any(0, &conn->addr);
                conn->state = NETCONN_STATE_WAITING;
            }
            conn->port      = skd->port;
            conn->localport = skd->localport;
            conn->idle_time = skd->timeout;
            conn->skd       = skd;
            // conn->cmd_mode = cmd_mode;

            net_msg = tls_mem_alloc(sizeof(*net_msg));
            if (net_msg == NULL) {
                net_free_socket(conn->skt_num);
                return -1;
            }
            memset(net_msg, 0, sizeof(*net_msg));
            net_msg->skt_no = conn->skt_num;
            err = tcpip_callback_with_block(do_create_connect, net_msg, 0);
            if (err) {
                conn->state = NETCONN_STATE_NONE;
                net_free_socket(conn->skt_num);
                tls_mem_free(net_msg);
            }
        } else {
            return 0;
        }
    } else {
        err = ERR_VAL;
    }

    if (conn)
        return conn->skt_num;
    else
        return -1;
}

int tls_socket_get_status(u8 socket, u8 *buf, u32 bufsize)
{
    struct tls_skt_status_t *    skt_status;
    struct tls_skt_status_ext_t *skts_ext;
    struct tls_netconn *         conn;
    struct tls_netconn *         client_conn;
    struct tls_netconn *         server_conn;
    int                          remain_len;

    skt_status = (struct tls_skt_status_t *) buf;

    if (bufsize < sizeof(struct tls_skt_status_t)) {
        TLS_DBGPRT_ERR("\nbufsize=%d\n", bufsize);
        return ERR_VAL;
    }

    if (socket < 1 || socket > 20) {
        TLS_DBGPRT_ERR("\nskt num=%d\n", socket);
        return ERR_VAL;
    }

    memset(buf, 0, bufsize);

    conn = tls_net_get_socket(socket);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("conn==%x,used=%d\n", conn, conn->used);
        return ERR_VAL;
    }
    remain_len = bufsize - sizeof(u32);
    skts_ext   = (struct tls_skt_status_ext_t *) (buf + sizeof(u32));
    if (conn == NULL) {
        skt_status->socket_cnt   = 1;
        skts_ext->protocol       = SOCKET_PROTO_TCP;
        skts_ext->status         = 0;
        skts_ext->socket         = socket;
        skts_ext->host_ipaddr[0] = 0;
        skts_ext->host_ipaddr[1] = 0;
        skts_ext->host_ipaddr[2] = 0;
        skts_ext->host_ipaddr[3] = 0;
        return 0;
    }
    skts_ext->protocol = (enum tls_socket_protocol) conn->proto;
    // TLS_DBGPRT_INFO("conn->client=%d, conn->state=%d, conn->used=%d\n",
    // conn->client, conn->state,conn->used);
    if (conn->client) {
        skt_status->socket_cnt = 1;
        if (conn->used) {
            MEMCPY(skts_ext->host_ipaddr, (u8 *) ip_2_ip4(&conn->addr), 4);
            //            server_conn = dl_list_first(&conn->list, struct
            //            tls_netconn, list);
            server_conn = get_server_conn(conn);
            if (server_conn != NULL) {
                //     TLS_DBGPRT_INFO("server_conn->skt_num = %d\n",
                //     server_conn->skt_num);
                skts_ext->local_port = server_conn->localport;
            } else
                skts_ext->local_port = conn->localport;
            skts_ext->remote_port = conn->port;
            skts_ext->status      = conn->state; /* connected */
            skts_ext->socket      = conn->skt_num;
        } else {
            skts_ext->status = conn->state; /* disconnect */
            skts_ext->socket = conn->skt_num;
        }
        // TLS_DBGPRT_INFO("skts_ext->socket=%d, skts_ext->status=%d,
        // conn->state=%d\n", skts_ext->socket, skts_ext->status, conn->state);
    } else {
        struct tls_ethif *ethif;
        ethif                  = tls_netif_get_ethif();
        skt_status->socket_cnt = 1;
        if (conn->used) {
            skts_ext->status = conn->state; /* listen */
            // MEMCPY(skts_ext->host_ipaddr, &conn->addr.addr, 4);
            // MEMCPY(skts_ext->host_ipaddr, (char
            // *)&sys->ethif->nif->ip_addr.addr, 4); MEMCPY(&conn->addr.addr,
            // (char *)&sys->ethif->nif->ip_addr.addr, 4);
            // MEMCPY(skts_ext->host_ipaddr, (char *)&ethif->ip_addr.addr, 4);
            MEMCPY(skts_ext->host_ipaddr, &ethif->ip_addr, 4);
            if (conn->proto == 1) {
                skts_ext->remote_port = conn->port;
                skts_ext->local_port  = conn->localport;
                if (skts_ext->local_port == 0)
                    skts_ext->local_port = skts_ext->remote_port;
                MEMCPY(skts_ext->host_ipaddr, IP_ADDR_BROADCAST, 4);
            } else {
                skts_ext->local_port  = conn->port;
                skts_ext->remote_port = 0;
            }
            skts_ext->socket = conn->skt_num;

            remain_len -= sizeof(struct tls_skt_status_ext_t);

            dl_list_for_each(
                client_conn, &conn->list, struct tls_netconn, list)
            {
                if (remain_len < sizeof(struct tls_skt_status_ext_t))
                    break;
                if (client_conn->used) {
                    skts_ext++;
                    skts_ext->status = client_conn->state; /* connect */
                    skt_status->socket_cnt++;
                    MEMCPY(skts_ext->host_ipaddr,
                           (char *) ip_2_ip4(&client_conn->addr),
                           4);
                    skts_ext->local_port  = conn->port;
                    skts_ext->remote_port = client_conn->port;
                    skts_ext->socket      = client_conn->skt_num;
                    skts_ext->protocol
                        = (enum tls_socket_protocol) client_conn->proto;
                    remain_len -= sizeof(struct tls_skt_status_ext_t);
                }
            }
        } else {
            skts_ext->status = conn->state;
            skts_ext->socket = conn->skt_num;
        }
    }
    return 0;
}

struct tls_netconn *tls_net_get_socket(u8 socket)
{
    struct tls_netconn *conn = NULL;

    if (socket < 1 || socket > TLS_MAX_NETCONN_NUM) //* 2
    {
        TLS_DBGPRT_ERR("skt num=%d\n", socket);
        return NULL;
    }

    conn = p_net_conn[socket - 1];
    // TLS_DBGPRT_INFO("conn->skt_num = %d\n", conn->skt_num);
    return conn;
}

int tls_socket_close(u8 socket)
{
    struct tls_net_msg *net_msg;
    struct tls_netconn *conn;
    err_t               err;
    // TLS_DBGPRT_INFO("socket=%d.\n", socket);
    if (socket < 1 || socket > TLS_MAX_NETCONN_NUM) //*2
    {
        TLS_DBGPRT_ERR("skt num=%d\n", socket);
        return -1;
    }

    conn = tls_net_get_socket(socket);

    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("conn==%x,used=%d\n", conn, conn->used);
        return -1;
    }
    net_msg = tls_mem_alloc(sizeof(*net_msg));
    if (net_msg == NULL) {
        TLS_DBGPRT_ERR("\nmem err\n");
        return -1;
    }
    memset(net_msg, 0, sizeof(*net_msg));
    net_msg->skt_no = socket;
    err             = tcpip_callback_with_block(do_close_connect, net_msg, 0);
    if (err) {
        tls_mem_free(net_msg);
    }

    return err;
}

static err_t netconn_msg(tcpip_callback_fn   function,
                         struct tls_net_msg *net_msg,
                         u8_t                block)
{
    err_t               err  = ERR_OK;
    struct tls_netconn *conn = NULL;
    conn                     = tls_net_get_socket(net_msg->skt_no);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("netconn_msg conn=%x,used=%d\n", conn, conn->used);
        return ERR_ARG;
    }
#    if 1
    err = tcpip_callback_with_block(function, net_msg, block);
    if (err) {
        // TLS_DBGPRT_INFO("free net_msg->dataptr=%p\n", net_msg->dataptr);
        // tls_mem_free(net_msg->dataptr);
        TLS_DBGPRT_INFO("callback end, err=%d\n", err);
        //	tls_mem_free(net_msg);
        return err;
    }
#        if CONN_SEM_NOT_FREE
    sys_arch_sem_wait(&conn_op_completed[net_msg->skt_no - 1], 0);
#        else
    conn = tls_net_get_socket(net_msg->skt_no);
    if (conn != NULL && conn->op_completed != NULL) {
        sys_arch_sem_wait(&conn->op_completed, 0);
        conn = tls_net_get_socket(net_msg->skt_no);
        if (conn == NULL || TRUE != conn->used) {
            printf("wait error, conn is freeed\n");
        }
    }
#        endif
    err = net_msg->err;
    return err;
#    else
    struct tls_net_msg *net_msg_item;
    if (conn->current_msg == NULL) {
        conn->current_msg = net_msg;
    } else {
        dl_list_add_tail(&conn->current_msg->list, &net_msg->list);
    }
    // TLS_DBGPRT_INFO("callback\n");
    err = tcpip_callback_with_block(function, conn->current_msg, block);
    if (err) {
        TLS_DBGPRT_INFO("callback end, err=%d\n", err);
        return err;
    }
    sys_arch_sem_wait(&conn->op_completed, 0);
    // TLS_DBGPRT_INFO("callback end\n");
    if (conn->current_msg->err)
        return conn->current_msg->err;

    dl_list_for_each(
        net_msg_item, &conn->current_msg->list, struct tls_net_msg, list)
    {
        err = tcpip_callback_with_block(function, net_msg_item, block);
        if (err)
            break;
        sys_arch_sem_wait(&conn->op_completed, 0);
        if (net_msg_item->err)
            break;
        dl_list_del(&net_msg_item->list);
        tls_mem_free(net_msg_item);
    }
    if (dl_list_empty(&conn->current_msg->list)) {
        tls_mem_free(conn->current_msg);
        conn->current_msg = NULL;
    } else {
        tls_mem_free(conn->current_msg);
        conn->current_msg = net_msg_item;
    }
    return ERR_OK;
#    endif
}

int tls_socket_udp_sendto(u16   localport,
                          u8 *  ip_addr,
                          u16   port,
                          void *pdata,
                          u16   len)
{
    struct tls_netconn *conn;
    struct tls_net_msg *net_msg = NULL;
    struct pbuf *       p;
    int                 i;
    int                 err   = 0;
    bool                found = false;

    /* find match socket */
    for (i = 0; i < TLS_MAX_NETCONN_NUM; i++) { // * 2
        conn = tls_net_get_socket(i);
        if (conn != NULL && conn->used && (localport == conn->port)
            && (conn->proto == TLS_NETCONN_UDP)) {
            found = true;
            break;
        }
    }
    if (!found) {
        TLS_DBGPRT_ERR("\nfound=0\n");
        err = -1;
        goto out;
    }

    net_msg = tls_mem_alloc(sizeof(struct tls_net_msg));
    if (!net_msg)
        return ERR_MEM;

    net_msg->len     = len;
    net_msg->dataptr = pdata;

    /* udp */
    while (1) {
        p = pbuf_alloc(PBUF_TRANSPORT, net_msg->len, PBUF_REF);
        if (p != NULL) {
            break;
        } else {
            /* delay 1 ticks */
            tls_os_time_delay(1);
        }
    }
    p->payload = (void *) net_msg->dataptr;
    p->len = p->tot_len = net_msg->len;

    net_msg->err    = ERR_VAL; /* for debug : catch not set err */
    net_msg->p      = p;
    net_msg->port   = port;
    net_msg->skt_no = conn->skt_num;
    MEMCPY((char *) &(ip_2_ip4(&net_msg->addr)->addr), ip_addr, 4);
    IP_SET_TYPE(&net_msg->addr, IPADDR_TYPE_V4);
    err = netconn_msg(net_do_send, net_msg, 0);
out:
    if (net_msg) /*Who allocates free it*/
        tls_mem_free(net_msg);
    return err;
}

int tls_socket_send(u8 skt_num, void *pdata, u16 len)
{
    struct tls_net_msg  net_msg[1] = { 0 };
    struct tls_netconn *conn;
    struct tls_netconn *client_conn;
    struct pbuf *       p;
    err_t               err = ERR_OK;
    u32                 socket;
    u32                 errornum = 0;

    socket = skt_num;

    dl_list_init(&net_msg->list);
    net_msg->len     = len;
    net_msg->dataptr = pdata;
    if (socket < 1 || socket > TLS_MAX_NETCONN_NUM) //*2
    {
        TLS_DBGPRT_ERR("\nskt num=%d\n", socket);
        return ERR_VAL;
    }

    conn = tls_net_get_socket(socket);
    if (conn == NULL || TRUE != conn->used) {
        TLS_DBGPRT_ERR("\nconn=%x,used=%d\n", conn, conn->used);
        return ERR_VAL;
    }
    if (conn->proto == TLS_NETCONN_UDP) {
        net_msg->skt_no = skt_num;
        net_msg->err    = ERR_VAL; /* for debug : catch not set err */
        do {
            p = pbuf_alloc(PBUF_TRANSPORT, net_msg->len, PBUF_REF);
            if (p != NULL) {
                break;
            } else {
                TLS_DBGPRT_INFO("pbuf_alloc err.\n");
                return ERR_VAL;
            }
        } while (0);
        p->payload = (void *) net_msg->dataptr;
        p->len = p->tot_len = net_msg->len;

        net_msg->p    = p;
        net_msg->port = conn->port;
        //    ip_addr_set(&net_msg->addr, &conn->addr);
        ip_addr_copy(net_msg->addr, conn->addr);

        err = netconn_msg(net_do_send, net_msg, 0);
    } else if (conn->proto == TLS_NETCONN_TCP) {
        if (conn->client) {
            net_msg->skt_no = skt_num;
            net_msg->err    = ERR_VAL; /* for debug : catch not set err */
            err             = netconn_msg(net_do_write, net_msg, 0);
        } else {
            dl_list_for_each(
                client_conn, &conn->list, struct tls_netconn, list)
            {
                // printf("\nclient conn=%x,used==%d, sock num=%d, net
                // conn=%x\n",client_conn, client_conn->used,
                // client_conn->skt_num, p_net_conn[client_conn->skt_num - 1]);
                if (client_conn && TRUE == client_conn->used
                    && client_conn == p_net_conn[client_conn->skt_num - 1]) {
                    net_msg->skt_no = client_conn->skt_num;
                    net_msg->err = ERR_VAL; /* for debug : catch not set err */
                    err          = netconn_msg(net_do_write, net_msg, 0);
                    if (err) {
                        //		TLS_DBGPRT_ERR("\nerr:%d\n",err);
                    }
                } else {
                    struct tls_netconn *conn_tmp;
                    conn_tmp = tls_net_get_socket(socket);
                    errornum++;
                    if (NULL == conn_tmp || errornum >= 4) {
                        TLS_DBGPRT_ERR(
                            "\nconn=%x, errnum=%d\n", conn_tmp, errornum);
                        err = ERR_VAL;
                        break;
                    }
                }
            }
        }
    } else {
        err = -1;
    }
    return err;
}

int tls_net_init()
{
    // int i;
    memset(p_net_conn, 0, sizeof(struct tls_netconn *) * TLS_MAX_NETCONN_NUM);
    return 0;
}

#endif // TLS_CONFIG_SOCKET_RAW
